import { useGlobSetting } from '/@/hooks/setting';
import { dataTransmissionCipher } from '/@/settings/encryptionSetting';
import { RsaEncryption, AesEncryption, SM4Encryption, SM2Encryption } from '/@/utils/cipher';
import Base64 from 'crypto-js/enc-base64';
import UTF8 from 'crypto-js/enc-utf8';

const globSetting = useGlobSetting();
const dataTransmissionDataMode = globSetting.dataTransmissionDataMode;
const isDebug = false;

export enum CryptoTypeEnum {
  RSA = 'RSA',
  AES = 'AES',
  SM2 = 'SM2',
  SM4 = 'SM4',
}

export interface CryptoParams {
  type: CryptoTypeEnum;
  privatekey: string;
  publickey: string;
  key: string;
  iv: string;
}

export class CryptoUtils {
  private cryptoType = CryptoTypeEnum.RSA;
  private rsaEncryption = new RsaEncryption({ publickey: dataTransmissionCipher.publickey, privatekey: dataTransmissionCipher.privatekey });
  private aesEcnryption = new AesEncryption({ key: dataTransmissionCipher.key, iv: dataTransmissionCipher.iv });
  private sm2Encryption = new SM2Encryption({ publickey: dataTransmissionCipher.publickey, privatekey: dataTransmissionCipher.privatekey });
  private sm4Ecnryption = new SM4Encryption({ key: dataTransmissionCipher.key, iv: dataTransmissionCipher.iv });

  constructor(opt: Partial<CryptoParams> = {}) {
    const { type, privatekey, publickey, key, iv } = opt;
    if (isDebug) {
      console.log('cryptoUtils constructor type is ', type, 'defatult type is ', dataTransmissionDataMode);
    }

    if (type) {
      this.cryptoType = type;
      if (type == CryptoTypeEnum.RSA && publickey && privatekey) {
        this.rsaEncryption = new RsaEncryption({ publickey: publickey, privatekey: privatekey });
      } else if (type == CryptoTypeEnum.AES && (key || iv)) {
        this.aesEcnryption = new AesEncryption({ key: key, iv: iv });
      } else if (type == CryptoTypeEnum.SM2 && publickey && privatekey) {
        this.sm2Encryption = new SM2Encryption({ publickey: publickey, privatekey: privatekey });
      } else if (type == CryptoTypeEnum.SM4 && (key || iv)) {
        this.sm4Ecnryption = new SM4Encryption({ key: key, iv: iv });
      }
    } else if (dataTransmissionDataMode.toUpperCase() == 'RSA') {
      this.cryptoType = CryptoTypeEnum.RSA;
    } else if (dataTransmissionDataMode.toUpperCase() == 'AES') {
      this.cryptoType = CryptoTypeEnum.AES;
    } else if (dataTransmissionDataMode.toUpperCase() == 'SM2') {
      this.cryptoType = CryptoTypeEnum.SM2;
    } else if (dataTransmissionDataMode.toUpperCase() == 'SM4') {
      this.cryptoType = CryptoTypeEnum.SM4;
    }
  }

  encrypt(cipherText: string) {
    return this.encrypt2(cipherText, this.cryptoType);
  }

  encrypt2(cipherText: string, type: CryptoTypeEnum) {
    if (isDebug) {
      console.info('encrypt2 input type= ', type, 'data= ', cipherText);
    }
    let encrypted: any = encodeURIComponent(cipherText);
    if (type == CryptoTypeEnum.RSA) {
      encrypted = this.rsaEncryption.encryptLong(encrypted);
    } else if (type == CryptoTypeEnum.AES) {
      encrypted = this.aesEcnryption.encrypt(encrypted);
    } else if (type == CryptoTypeEnum.SM2) {
      encrypted = this.sm2Encryption.encrypt(encrypted);
    } else if (type == CryptoTypeEnum.SM4) {
      encrypted = this.sm4Ecnryption.encrypt(encrypted);
    }
    if (isDebug) {
      console.info('encrypt2 output type= ', type, 'data= ', encrypted);
    }
    return encrypted;
  }

  /**
   *
   * @param cipherText 加密数据
   * @param keyText 加密数据的KEY
   * @param keyType 加密KEY的算法
   * @param dataType 加密数据的算法
   * @param keyEntrypted 是否需要加密KEY
   * @returns
   */
  encryptByMixed(
    cipherText: string,
    keyText: string,
    keyType: CryptoTypeEnum,
    dataType: CryptoTypeEnum,
    keyEntrypted: boolean
  ): { key: string; data: string } {
    if (isDebug) {
      console.info('encryptByMixed input keyType= ', keyType, 'dataType= ', dataType, 'data= ', cipherText);
    }

    const encrypted = { key: '', data: '' };
    encrypted.data = encodeURIComponent(cipherText);
    encrypted.key = keyText;
    if (dataType == CryptoTypeEnum.AES) {
      const newAesEcnryption = new AesEncryption({ key: encrypted.key, iv: '' });
      encrypted.data = newAesEcnryption.encrypt(encrypted.data);
    } else if (dataType == CryptoTypeEnum.SM4) {
      const newSm4Ecnryption = new SM4Encryption({ key: encrypted.key, iv: '' });
      encrypted.data = newSm4Ecnryption.encrypt(encrypted.data);
    } else {
      throw new Error('key encrypt type ' + dataType + ' is not supported');
    }

    if (keyEntrypted) {
      encrypted.key = encodeURIComponent(encrypted.key);
      if (keyType == CryptoTypeEnum.RSA) {
        encrypted.key = this.rsaEncryption.encryptLong(encrypted.key);
      } else if (keyType == CryptoTypeEnum.SM2) {
        encrypted.key = this.sm2Encryption.encrypt(encrypted.key);
      } else {
        throw new Error('data encrypt type ' + keyType + ' is not supported');
      }
    }

    if (isDebug) {
      console.info('encryptByMixed output keyType= ', keyType, 'dataType= ', dataType, 'data= ', encrypted);
    }
    return encrypted;
  }

  decrypt(cipherText: string) {
    return this.decrypt2(cipherText, this.cryptoType);
  }

  decrypt2(cipherText: string, type: CryptoTypeEnum) {
    if (isDebug) {
      console.info('decrypt2 input type= ', type, 'data= ', cipherText);
    }
    let decrypted = cipherText;
    if (type == CryptoTypeEnum.RSA) {
      decrypted = this.rsaEncryption.decryptLong(decrypted);
    } else if (type == CryptoTypeEnum.AES) {
      decrypted = this.aesEcnryption.decrypt(decrypted);
    } else if (type == CryptoTypeEnum.SM2) {
      decrypted = this.sm2Encryption.decrypt(decrypted);
    } else if (type == CryptoTypeEnum.SM4) {
      decrypted = this.sm4Ecnryption.decrypt(decrypted);
    }
    //把加号全局替换为%20，否则转码后空格会变成加号
    decrypted = decodeURIComponent(decrypted?.replace(/\+/g, '%20'));
    if (isDebug) {
      console.info('decrypt2 output type= ', type, 'data= ', decrypted);
    }
    return decrypted;
  }

  decryptByMixed(cipherText: string, keyText: string, keyType: CryptoTypeEnum, dataType: CryptoTypeEnum): { key: string; data: string } {
    if (isDebug) {
      console.info('decryptByMixed input keyType= ', keyType, 'dataType= ', dataType, 'data= ', cipherText);
    }

    const decrypted = { key: '', data: '' };
    decrypted.key = keyText;
    if (keyType == CryptoTypeEnum.RSA) {
      decrypted.key = this.rsaEncryption.decryptLong(decrypted.key);
    } else if (keyType == CryptoTypeEnum.SM2) {
      decrypted.key = this.sm2Encryption.decrypt(decrypted.key);
    } else {
      throw new Error('decryptByMixed key decrypt type ' + keyType + ' is not supported');
    }
    decrypted.key = decodeURIComponent(decrypted.key?.replace(/\+/g, '%20'));
    if (isDebug) {
      console.info('decryptByMixed output key= ', decrypted.key);
    }

    if (dataType == CryptoTypeEnum.AES) {
      const newAesEcnryption = new AesEncryption({ key: decrypted.key, iv: '' });
      decrypted.data = newAesEcnryption.decrypt(cipherText);
    } else if (dataType == CryptoTypeEnum.SM4) {
      const newSm4Ecnryption = new SM4Encryption({ key: decrypted.key, iv: '' });
      decrypted.data = newSm4Ecnryption.decrypt(cipherText);
    } else {
      throw new Error('decryptByMixed data encrypt type ' + dataType + ' is not supported');
    }
    decrypted.data = decodeURIComponent(decrypted.data?.replace(/\+/g, '%20'));
    if (isDebug) {
      console.info('decryptByMixed output data= ', decrypted.data);
    }

    if (isDebug) {
      console.info('decryptByMixed output keyType', keyType, 'dataType= ', dataType, 'data= ', decrypted);
    }
    return decrypted;
  }

  //随机生成指定数量的16进制key
  generatekey() {
    const num = 16;
    const library = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let key = '';
    for (let i = 0; i < num; i++) {
      const randomPoz = Math.floor(Math.random() * library.length);
      key += library.substring(randomPoz, randomPoz + 1);
    }
    const wordArray = UTF8.parse(key);
    return Base64.stringify(wordArray);
  }
}
